package com.piece.core.mongo.util;

import com.piece.core.framework.constant.ApplicationConstants;
import com.piece.core.framework.constant.RegisterConstants;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.support.dto.PayloadDTO;
import com.piece.core.framework.util.math.SequenceUtil;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.log.enums.MessageLogType;
import com.piece.core.mongo.entity.MongoMessageLog;
import com.piece.core.mongo.service.MongoMessageLogService;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.time.DateUtil;
import com.piece.core.framework.util.basic.SpringUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.message.MessageOperator;
import com.piece.core.framework.properties.ApplicationProperties;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
import java.util.Date;

@Slf4j
public class MongoMessageOperator implements MessageOperator {

    private ApplicationProperties applicationProperties;
    private MongoMessageLogService mongoMessageLogService;

    public MongoMessageOperator(ApplicationProperties applicationProperties, MongoMessageLogService mongoMessageLogService) {
        this.applicationProperties = applicationProperties;
        this.mongoMessageLogService = mongoMessageLogService;
    }

    @Override
    public void prepareSend(PayloadDTO payload, String messageType) {
        MongoMessageLog messageLog = JsonUtil.toBean(payload.getEntity(), MongoMessageLog.class);
        messageLog.setLogId(SequenceUtil.uuid());
        messageLog.setApplicationName(applicationProperties.getValue(ApplicationConstants.APPLICATION_ID));

        if (-1 != messageLog.getRetryCount()) {
            messageLog.setRetryCount(0);
        }

        messageLog.setMessageType(messageType);
        if (messageType.contains("kafka")) {
            String topic = payload.getTopic();
            if (StringUtil.isEmpty(topic)) {
                topic = messageLog.getTopic();
            }
            payload.setTopic(topic);
            messageLog.setTopic(topic);
        } else {
            String exchange = payload.getExchange();
            if (StringUtil.isEmpty(exchange)) {
                exchange = Convert.toStr(messageLog.getExchange(), RegisterConstants.DELAY_EXCHANGE_NAME);
            }
            payload.setExchange(exchange);
            messageLog.setExchange(exchange);

            String router = payload.getRouter();
            if (StringUtil.isEmpty(router)) {
                router = Convert.toStr(messageLog.getRouter(), RegisterConstants.DELAY_ROUTE_NAME);
            }
            payload.setRouter(router);
            messageLog.setRouter(router);
        }

        if (StringUtil.isEmpty(messageLog.getType())) {
            messageLog.setType(MessageLogType.MESSAGE.getType() + "");
        }
        messageLog.setStartTime(new Date());
        messageLog.setStatus(StatusType.RUN.getCode());
        messageLog = mongoMessageLogService.insert(messageLog);
        payload.setEntity(JsonUtil.toJson(messageLog));
    }

    @Override
    public void doReceive(PayloadDTO payload) {
        MongoMessageLog messageLog = JsonUtil.toBean(payload.getEntity(), MongoMessageLog.class);

        try {
            if (StringUtil.isNotEmpty(messageLog.getMethod())) {
                String[] beanMethods = messageLog.getMethod().split("[.]");
                String beanId = beanMethods[0];
                String methodName = beanMethods[1];
                Object serviceBean = SpringUtil.getBean(beanId);
                Method invokeMethod = serviceBean.getClass().getDeclaredMethod(methodName, String.class);
                invokeMethod.invoke(serviceBean, messageLog.getEntity());
            }

            messageLog.setEndTime(new Date());
            long execTime = messageLog.getEndTime().getTime() - new Date().getTime();
            messageLog.setExecTime(DateUtil.getDescriptionTime(execTime));
            messageLog.setStatus(StatusType.SUCCESS.getCode());
        } catch (Exception e) {
            int retryCount = messageLog.getRetryCount();
            if (-1 == retryCount) {
                messageLog.setStatus(StatusType.FAIL.getCode());
                messageLog.setDesc(e.getMessage());
            } else if (retryCount == Convert.toInt(applicationProperties.getValue(ApplicationConstants.SYSTEM_MESSAGE_RETRY), 3)) {
                messageLog.setStatus(StatusType.FAIL.getCode());
                messageLog.setDesc(e.getMessage());
            } else {
                messageLog.setStatus(StatusType.RUN.getCode());
                messageLog.setRetryCount(retryCount + 1);
            }

            throw new RuntimeException(e);
        } finally {
            messageLog = mongoMessageLogService.update(messageLog);
            payload.setEntity(JsonUtil.toJson(messageLog));
        }
    }

    @Override
    public boolean checkRetryLimit(PayloadDTO payload) {
        MongoMessageLog messageLog = JsonUtil.toBean(payload.getEntity(), MongoMessageLog.class);
        if (StatusType.FAIL.getCode().equals(messageLog.getStatus())) {
            return false;
        }
        return true;
    }
}
